﻿using Autofac;
using MgSoft.K3Cloud.WebApi.Dto;
using MgSoft.K3Cloud.WebApi.Exceptions;
using MgSoft.Log;
using MgSoft.Util;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MgSoft.K3Cloud.WebApi
{
    public abstract class BaseApi : IWebApi
    {
        protected abstract string formId { get; }
        public ILifetimeScope lifetimeScope { get; set; }
        public K3CloudApiExecuter K3CloudClient => lifetimeScope.Resolve<K3CloudApiExecuter>();
        public ApiServerInfo ApiServerInfo => lifetimeScope.Resolve<ApiServerInfo>();
        protected KingdeeClientInfoMemoryService kingdeeClientInfoMemoryService => lifetimeScope.Resolve<KingdeeClientInfoMemoryService>();
        protected IMgLog log => lifetimeScope.Resolve<IMgLog>();

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="getListInputDto"></param>
        /// <param name="serializeIndexFields">
        /// 序列化的顺序，需要配合FieldKeys进行修改
        /// 由于api返回的结果是一个扁平的数组
        /// indexFields的作用是，使行数组的位置与要序列化的字段作对应，例如:BillNo在第0位，把数组的第0位序列话给BillNo
        /// </param>
        /// <returns></returns>
        public List<T> GetList<T>(GetListInputDto getListInputDto) where T : class, new() => GetList<T>(getListInputDto, CancellationToken.None);
        public List<T> GetList<T>(GetListInputDto getListInputDto, CancellationToken cancellationToken) where T : class, new()
        {
            setFormId(getListInputDto);

            string fileds = getListInputDto.FieldKeys;
            if (fileds == null || fileds.Length == 0)
            {
                fileds = getFieldsByPropertyMapName<T>();
            }
            getListInputDto.FieldKeys = fileds;

            var apiResult = K3CloudClient.ExecuteBillQuery(kingdeeClientInfoMemoryService.Get(), JsonConvert.SerializeObject(getListInputDto), cancellationToken);

            CheckGetListIsSuccess(apiResult);

            return SerializeToPocoList<T>(apiResult, fileds);
        }


        public T Get<T>(string formId, long id) where T : class => Get<T>(formId, id, CancellationToken.None);
        public T Get<T>(string formId, long id, CancellationToken cancellationToken) where T : class
        {
            return Get<T>(new GetInputDto()
            {
                Id = id
            });
        }

        public T Get<T>(string formId, string number) where T : class => Get<T>(formId, number, CancellationToken.None);
        public T Get<T>(string formId, string number, CancellationToken cancellationToken) where T : class
        {
            return Get<T>(new GetInputDto()
            {
                Number = number
            });
        }

        public T Get<T>(GetInputDto getInputDto) where T : class => Get<T>(getInputDto, CancellationToken.None);
        public T Get<T>(GetInputDto getInputDto, CancellationToken cancellationToken) where T : class
        {
            setFormId(getInputDto);

            var apiResult = K3CloudClient.View(kingdeeClientInfoMemoryService.Get(), getInputDto.FormId, JsonConvert.SerializeObject(getInputDto));

            CheckGetIsSuccess(apiResult);

            var jObject = JObject.Parse(apiResult);
            var data = jObject["Result"]["Result"].ToString();
            //var ret = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(data);
            return JsonConvert.DeserializeObject<T>(data);
        }

        public bool Exist(string filterString, string fileds = "FId") => Exist(filterString, CancellationToken.None, fileds);
        public bool Exist(string filterString, CancellationToken cancellationToken, string fileds = "FId")
        {
            var getListInputDto = new GetListInputDto()
            {
                FieldKeys = fileds,
                FilterString = filterString
            };

            setFormId(getListInputDto);
            var apiResult = K3CloudClient.ExecuteBillQuery(kingdeeClientInfoMemoryService.Get(), JsonConvert.SerializeObject(getListInputDto));

            CheckGetListIsSuccess(apiResult);

            var result = JsonConvert.DeserializeObject<List<List<string>>>(apiResult);
            return result.Count > 0;
        }

        /// <summary>
        /// 暂存
        /// </summary>
        /// <param name="saveInputDto"></param>
        /// <returns></returns>
        public virtual List<DraftOutputDto> Draft(DraftInputDto draftInputDto) => Draft(draftInputDto, CancellationToken.None);
        public virtual List<DraftOutputDto> Draft(DraftInputDto draftInputDto, CancellationToken cancellationToken)
        {
            setFormId(draftInputDto);


            var apiResult = K3CloudClient.Draft(kingdeeClientInfoMemoryService.Get(), draftInputDto.FormId, JsonConvert.SerializeObject(draftInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<DraftOutputDto>>(GetData(apiResult));
        }

        /// <summary>
        /// 保存
        /// </summary>
        /// <param name="saveInputDto"></param>
        /// <returns></returns>
        public virtual List<SaveOutPutDto> Save(SaveInputDto saveInputDto) => Save(saveInputDto, CancellationToken.None);
        public virtual List<SaveOutPutDto> Save(SaveInputDto saveInputDto, CancellationToken cancellationToken)
        {
            setFormId(saveInputDto);

            var apiResult = K3CloudClient.Save(kingdeeClientInfoMemoryService.Get(), saveInputDto.FormId, JsonConvert.SerializeObject(saveInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<SaveOutPutDto>>(GetData(apiResult));
        }

        /// <summary>
        /// 保存
        /// </summary>
        /// <param name="saveInputDto"></param>
        /// <returns></returns>
        public virtual Tuple<List<SaveOutPutDto>, List<TNeedReturnDataOutputDto>> Save<TNeedReturnDataOutputDto>(SaveInputDto saveInputDto) where TNeedReturnDataOutputDto : NeedReturnDataOutputDto => Save<TNeedReturnDataOutputDto>(saveInputDto, CancellationToken.None);
        public virtual Tuple<List<SaveOutPutDto>, List<TNeedReturnDataOutputDto>> Save<TNeedReturnDataOutputDto>(SaveInputDto saveInputDto, CancellationToken cancellationToken) where TNeedReturnDataOutputDto : NeedReturnDataOutputDto
        {
            setFormId(saveInputDto);

            var mapNameList = ReflectionUtil.GetPropertyMapNameAttributeNameList<TNeedReturnDataOutputDto>();
            saveInputDto.NeedReturnFields = mapNameList.ToArray();

            var apiResult = K3CloudClient.Save(kingdeeClientInfoMemoryService.Get(), saveInputDto.FormId, JsonConvert.SerializeObject(saveInputDto));

            CheckGetIsSuccess(apiResult);
            CheckNeedReturnIsSuccess(apiResult);
            var saveOutputResult = JsonConvert.DeserializeObject<List<SaveOutPutDto>>(GetData(apiResult));
            var needReturnDataOutputResult = JsonConvert.DeserializeObject<List<TNeedReturnDataOutputDto>>(GetSaveOutputNeedReturnData(apiResult));

            return new Tuple<List<SaveOutPutDto>, List<TNeedReturnDataOutputDto>>(saveOutputResult, needReturnDataOutputResult);
        }

        /// <summary>
        /// 批量保存
        /// </summary>
        /// <param name="batchSaveInputDto"></param>
        /// <returns></returns>
        public virtual BatchSaveOutputDto BatchSave(BatchSaveInputDto batchSaveInputDto) => BatchSave(batchSaveInputDto, CancellationToken.None);
        public virtual BatchSaveOutputDto BatchSave(BatchSaveInputDto batchSaveInputDto, CancellationToken cancellationToken)
        {
            setFormId(batchSaveInputDto);

            var apiResult = K3CloudClient.BatchSave(kingdeeClientInfoMemoryService.Get(), batchSaveInputDto.FormId, JsonConvert.SerializeObject(batchSaveInputDto));
            //CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<BatchSaveOutputDto>(GetResponseStatus(apiResult));
        }

        /// <summary>
        /// 提交
        /// </summary>
        /// <param name="submitInputDto"></param>
        /// <returns></returns>
        public List<SubmitOutputDto> Submit(SubmitInputDto submitInputDto) => Submit(submitInputDto, CancellationToken.None);
        public List<SubmitOutputDto> Submit(SubmitInputDto submitInputDto, CancellationToken cancellationToken)
        {
            setFormId(submitInputDto);

            var apiResult = K3CloudClient.Submit(kingdeeClientInfoMemoryService.Get(), submitInputDto.FormId, JsonConvert.SerializeObject(submitInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<SubmitOutputDto>>(GetData(apiResult));
        }
        /// <summary>
        /// 审核
        /// </summary>
        /// <param name="auditInputDto"></param>
        /// <returns></returns>
        public List<AuditOutputDto> Audit(AuditInputDto auditInputDto) => Audit(auditInputDto, CancellationToken.None);
        public List<AuditOutputDto> Audit(AuditInputDto auditInputDto, CancellationToken cancellationToken)
        {
            setFormId(auditInputDto);

            var apiResult = K3CloudClient.Audit(kingdeeClientInfoMemoryService.Get(), auditInputDto.FormId, JsonConvert.SerializeObject(auditInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<AuditOutputDto>>(GetData(apiResult));
        }
        /// <summary>
        /// 反审核
        /// </summary>
        /// <param name="unAuditInputDto"></param>
        /// <returns></returns>
        public List<UnAuditOutputDto> UnAudit(UnAuditInputDto unAuditInputDto) => UnAudit(unAuditInputDto, CancellationToken.None);
        public List<UnAuditOutputDto> UnAudit(UnAuditInputDto unAuditInputDto, CancellationToken cancellationToken)
        {
            setFormId(unAuditInputDto);

            var apiResult = K3CloudClient.UnAudit(kingdeeClientInfoMemoryService.Get(), unAuditInputDto.FormId, JsonConvert.SerializeObject(unAuditInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<UnAuditOutputDto>>(GetData(apiResult));
        }
        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="deleteInputDto"></param>
        /// <returns></returns>
        public List<DeleteOutputDto> Delete(DeleteInputDto deleteInputDto) => Delete(deleteInputDto, CancellationToken.None);
        public List<DeleteOutputDto> Delete(DeleteInputDto deleteInputDto, CancellationToken cancellationToken)
        {
            setFormId(deleteInputDto);

            var apiResult = K3CloudClient.Delete(kingdeeClientInfoMemoryService.Get(), deleteInputDto.FormId, JsonConvert.SerializeObject(deleteInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<DeleteOutputDto>>(GetData(apiResult));
        }
        /// <summary>
        /// 下推
        /// </summary>
        /// <param name="PushInputDto"></param>
        /// <returns></returns>
        public List<PushOutputDto> Push(PushInputDto PushInputDto) => Push(PushInputDto, CancellationToken.None);
        public List<PushOutputDto> Push(PushInputDto PushInputDto, CancellationToken cancellationToken)
        {
            setFormId(PushInputDto);

            var apiResult = K3CloudClient.Push(kingdeeClientInfoMemoryService.Get(), PushInputDto.FormId, JsonConvert.SerializeObject(PushInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<PushOutputDto>>(GetData(apiResult));
        }

        /// <summary>
        /// 分配
        /// </summary>
        /// <param name="AllocateInputDto"></param>
        /// <returns></returns>
        public List<AllocateOutputDto> Allocate(AllocateInputDto allotInputDto) => Allocate(allotInputDto, CancellationToken.None);
        public List<AllocateOutputDto> Allocate(AllocateInputDto allotInputDto, CancellationToken cancellationToken)
        {
            setFormId(allotInputDto);

            var apiResult = K3CloudClient.Allocate(kingdeeClientInfoMemoryService.Get(), allotInputDto.FormId, JsonConvert.SerializeObject(allotInputDto));
            CheckGetIsSuccess(apiResult);

            return JsonConvert.DeserializeObject<List<AllocateOutputDto>>(GetData(apiResult));
        }


        #region 私有方法
        private void setFormId(BaseApiDto baseApiDto)
        {
            baseApiDto.FormId = this.formId;
        }

        private string getFieldsByPropertyMapName<T>() where T : class, new()
        {
            var mapNameList = ReflectionUtil.GetPropertyMapNameAttributeNameList<T>();

            if (mapNameList.Count == 0)
            {
                return "";
            }

            var result = new StringBuilder();
            foreach (var mapName in mapNameList)
            {
                result.Append($"{mapName},");
            }
            result.Remove(result.Length - 1, 1);

            return result.ToString();
        }

        private void CheckGetListIsSuccess(string apiResult)
        {
            JArray jResult = JArray.Parse(apiResult);
            if (jResult == null || jResult.Count == 0 || jResult[0].Count() == 0 || jResult[0][0].SelectToken("Result") == null)
            {
                return;
            }
            CheckIsSuccess(jResult[0][0].SelectToken("Result"));
        }

        private void CheckGetIsSuccess(string apiResult)
        {
            var jResult = JObject.Parse(apiResult)?["Result"];
            CheckIsSuccess(jResult);
        }

        private void CheckIsSuccess(JToken jResult)
        {
            if (jResult == null || jResult.SelectToken("ResponseStatus") == null)
            {
                return;
            }


            var responseStatus = jResult["ResponseStatus"];
            if (responseStatus == null || responseStatus.SelectToken("MsgCode") == null)
            {
                return;
            }

            var msgCode = responseStatus?["MsgCode"]?.Value<int>() ?? 0;
            if (msgCode.Equals(1))//是否超时。1 为超时。其他值暂不知晓，后续可以补充
            {
                throw new ApiException(getErrorMessage(responseStatus));
            }
            if (responseStatus.SelectToken("IsSuccess") != null && responseStatus["IsSuccess"].Value<bool>())
            {
                return;
            }
            if (responseStatus.SelectToken("Errors") != null)
            {
                //throw new ApiException(responseStatus["Errors"].ToString());                
                throw new ApiException(getErrorMessage(responseStatus));
            }
        }

        private void CheckNeedReturnIsSuccess(string apiResult)
        {
            var jResult = JObject.Parse(apiResult)?["Result"];
            var jArray = jResult["NeedReturnData"] as JArray;
            var sb = new StringBuilder();
            foreach (var item in jArray)
            {
                if (item.SelectToken("Exception") != null)
                {
                    if (!string.IsNullOrWhiteSpace(item["Exception"].ToString()))
                    {
                        sb.AppendLine(item["Exception"].ToString());
                    }
                }
            }

            if (sb.Length > 0) throw new ApiException($@"保存需返回的字段错误：{sb}");
        }

        private string GetData(string apiResult)
        {
            if (!string.IsNullOrEmpty(apiResult))
            {
                var jObject = JObject.Parse(apiResult);
                return jObject["Result"]["ResponseStatus"]["SuccessEntitys"].ToString();
            }
            return "";
        }

        private string GetResponseStatus(string apiResult)
        {
            if (!string.IsNullOrEmpty(apiResult))
            {
                var jObject = JObject.Parse(apiResult);
                return jObject["Result"]["ResponseStatus"].ToString();
            }
            return "";
        }

        private string GetSaveOutputNeedReturnData(string apiResult)
        {
            if (!string.IsNullOrEmpty(apiResult))
            {
                var jObject = JObject.Parse(apiResult);
                return jObject["Result"]["NeedReturnData"].ToString();
            }
            return "";
        }

        private List<T> SerializeToPocoList<T>(string queryList, string fieldKeys) where T : class, new()
        {
            var result = new List<T>();
            string[] fieldKeysArray = fieldKeys.Split(',');

            JArray jarray = JArray.Parse(queryList);

            foreach (var row in jarray)
            {
                T data = new T();
                for (int fieldIndex = 0; fieldIndex < fieldKeysArray.Length; fieldIndex++)
                {
                    string fieldKey = fieldKeysArray[fieldIndex];
                    ReflectionUtil.SetValueByAttribute(data, fieldKey, row[fieldIndex]);
                }

                result.Add(data);
            }

            return result;
        }


        private string getErrorMessage(JToken errorToken)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < errorToken["Errors"].Count(); i++)
            {
                sb.Append($"{i + 1}、{errorToken["Errors"][i]["Message"]}\r\n");
            }

            return sb.ToString();
        }
        #endregion
    }
}
